#include "chatwidget.h"
#include "ui_chatwidget.h"

#include "tcpclient.h"
#include <QHostInfo>
#include <QMessageBox>
#include <QScrollBar>
#include <QDateTime>
#include <QNetworkInterface>
#include <QProcess>
#include <QFileDialog>
#include<QtNetwork>

chatWidget::chatWidget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::chatWidget)
{
    ui->setupUi(this);

    // 创建一个 UDP 套接字，并将其作为当前 Widget 类的子对象
    udpSocket = new QUdpSocket(this);

    // 定义并设置 UDP 端口号为 45454
    // port = 45454;
    port = 45454;

    // 绑定 UDP 套接字到指定端口，允许地址共享和地址重用
    udpSocket->bind(port, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint);

    // 连接 UDP 套接字的 readyRead 信号到 processPendingDatagrams 槽函数，当有数据可读时调用 processPendingDatagrams 函数
    connect(udpSocket, SIGNAL(readyRead()), this, SLOT(processPendingDatagrams()));

    // 发送用户登录消息，类型为 NewParticipant
    sendMessage(NewParticipant);

    // 创建 TCP 文件传输服务对象，作为当前 Widget 类的子对象
    server = new TcpServer(this);

    // 连接 TCP 服务器的 sendFileName 信号到 getFileName 槽函数，当发送文件名时调用 getFileName 函数
    connect(server, SIGNAL(sendFileName(QString)), this, SLOT(getFileName(QString)));

}

chatWidget::~chatWidget()
{
    delete ui;
}


// 处理新用户加入的函数
void chatWidget::newParticipant(QString username, QString localHostName, QString idAddress)
{
    // 检查用户表中是否已经存在该用户
    bool isEmpty = ui->userTableWidget->findItems(localHostName, Qt::MatchExactly).isEmpty();
    if (isEmpty) {
        // 创建新的用户表项，用于存储用户名、主机名和 IP 地址
        QTableWidgetItem *user = new QTableWidgetItem(username);
        QTableWidgetItem *host = new QTableWidgetItem(localHostName);
        QTableWidgetItem *ip = new QTableWidgetItem(idAddress);

        // 在用户表的第一行插入新用户信息
        ui->userTableWidget->insertRow(0);
        ui->userTableWidget->setItem(0, 0, user);
        ui->userTableWidget->setItem(0, 1, host);
        ui->userTableWidget->setItem(0, 2, ip);

        // 设置消息浏览器的文本颜色和字体，并追加新用户在线的消息
        ui->messageBrowser->setTextColor(Qt::gray);
        ui->messageBrowser->setCurrentFont(QFont("Times New Roman", 10));
        ui->messageBrowser->append(tr("%1 在线！").arg(username));

        // 更新在线人数的显示
        ui->userNumLabel->setText(tr("在线人数：%1").arg(ui->userTableWidget->rowCount()));

        // 再次发送新用户加入的消息，可能用于通知其他用户
        sendMessage(NewParticipant);
    }
}

// 处理用户离开的函数
void chatWidget::participantLeft(QString username, QString localHostName, QString time)
{
    // 查找用户在用户表中的行号
    int rowNum = ui->userTableWidget->findItems(localHostName,Qt::MatchExactly).first()->row();

    // 从用户表中移除该用户所在的行
    ui->userTableWidget->removeRow(rowNum);

    // 设置消息浏览器的文本颜色和字体，并追加用户离开的消息
    ui->messageBrowser->setTextColor(Qt::gray);
    ui->messageBrowser->setCurrentFont(QFont("Time New Roman"));
    ui->messageBrowser->append(tr("%1 与 %2 离开！").arg(username).arg(time));
    // 更新在线人数的显示
    ui->userNumLabel->setText(tr("在线人数：%1").arg(ui->userTableWidget->rowCount()));
}

// 发送消息的函数，根据不同的消息类型发送不同的消息
void chatWidget::sendMessage(MessageType type, QString serverAddress)
{
    QByteArray data;
    QDataStream out(&data, QIODevice::WriteOnly);
    // 获取本地主机名
    QString localHostName = QHostInfo::localHostName();
    // 获取本地 IP 地址
    QString address = getIP();
    // 开始写入消息类型、用户名和本地主机名
    out << type << getUsername() << localHostName;

    // 根据消息类型进行不同的处理
    switch (type) {
    case Message:{
        // 对于普通消息，如果消息内容为空则弹出警告并返回
        if (ui->messageTextEdit->toPlainText() == "") {
            QMessageBox::warning(0, "警告", "发送内容不能为空", QMessageBox::Ok);
            return;
        }
        // 写入 IP 地址和消息内容
        out << address << getMessage();
        // 将消息浏览器的垂直滚动条滚动到最底部
        ui->messageBrowser->verticalScrollBar()->setValue(
            ui->messageBrowser->verticalScrollBar()->maximum());
        break;
    }
    case NewParticipant:
        // 对于新用户加入消息，仅写入 IP 地址
        out << address;
        break;
    case ParticipantLeft:
        // 对于用户离开消息，暂时不写入额外信息
        break;
    case Filename:{
        // 对于文件名消息，获取当前选中用户的 IP 地址，并写入 IP 地址、客户端地址和文件名
        int row = ui->userTableWidget->currentRow();
        QString clientAddress = ui->userTableWidget->item(row, 2)->text();
        out << address << clientAddress << fileName;
        break;
    }
    case Refuse:
        // 对于拒绝消息，写入服务器地址
        out << serverAddress;
        break;
    }

    // 通过 UDP 套接字将消息广播出去    //////////////////////////加入新ip地址可以实现通信
    udpSocket->writeDatagram(data, data.length(), QHostAddress::Broadcast, port);
    udpSocket->writeDatagram(data, data.length(), QHostAddress("192.168.1.48"), port);
    udpSocket->writeDatagram(data, data.length(), QHostAddress("192.168.1.51"), port);
}

// 获取本地 IP 地址的函数
QString chatWidget::getIP()
{
    // 获取系统中的所有 IP 地址列表
    QList<QHostAddress> list = QNetworkInterface::allAddresses();
    foreach (QHostAddress address, list) {
        // 筛选出 IPv4 地址
        if (address.protocol() == QAbstractSocket::IPv4Protocol)
            return address.toString();
    }
    // 如果没有找到 IPv4 地址，返回 0
    return 0;
}


// 获取系统用户名的函数
QString chatWidget::getUsername()
{
    // 存储可能包含用户名的环境变量名列表
    QStringList envVariables;
    envVariables << "USERNAME.*" << "USER.*" << "USERDOMAIN.*" << "HOSTNAME.*" << "DOMAINNAME.*";
    // 获取系统环境变量列表
    QStringList environment = QProcess::systemEnvironment();
    foreach (QString string, envVariables) {
        // 使用正则表达式查找匹配的环境变量
        int index = environment.indexOf(QRegularExpression(string));
        if (index!= -1) {
            QStringList stringList = environment.at(index).split('=');
            if (stringList.size() == 2) {
                // 找到匹配的环境变量，返回用户名
                return stringList.at(1);
                break;
            }
        }
    }
    // 如果未找到，返回 unknown
    return "unknown";
}

// 获取消息内容的函数
QString chatWidget::getMessage()
{
    // 获取输入框中的消息内容，以 HTML 格式
    QString msg = ui->messageTextEdit->toHtml();
    // 清空输入框并将焦点设置在输入框上
    ui->messageTextEdit->clear();
    ui->messageTextEdit->setFocus();
    return msg;
}

// 处理有文件等待接收的情况
void chatWidget::hasPendingFile(QString userName, QString serverAddress, QString clientAddress, QString fileName)
{
    // 获取本地 IP 地址
    QString ipAddress = getIP();
    if (ipAddress == clientAddress) {
        // 当本地 IP 地址与客户端地址相同时，弹出消息框询问用户是否接收文件
        int btn = QMessageBox::information(this, "接受文件",
                                           tr("来自%1（%2）的文件：%3，是否接收？")
                                               .arg(userName)
                                               .arg(serverAddress)
                                               .arg(fileName),
                                           QMessageBox::Yes, QMessageBox::No);
        if (btn == QMessageBox::Yes) {
            // 如果用户选择接收，打开文件保存对话框保存文件
            QString name = QFileDialog::getSaveFileName(0, "保存文件", fileName);
            if (!name.isEmpty()) {
                // 创建 TCP 客户端对象，设置文件名和服务器地址并显示客户端窗口
                TcpClient *client = new TcpClient(this);
                client->setFileName(name);
                client->setHostAddress(QHostAddress(serverAddress));
                client->show();
            }
        } else {
            // 如果用户拒绝接收，发送拒绝消息
            sendMessage(Refuse, serverAddress);
        }
    }
}

// 获取文件名并发送文件名消息
void chatWidget::getFileName(QString name)
{
    // 保存文件名并发送文件名消息
    fileName = name;
    sendMessage(Filename);
}

// 处理 UDP 接收到的数据报的函数
void chatWidget::processPendingDatagrams()
{
    // 循环处理所有等待处理的数据报
    while(udpSocket->hasPendingDatagrams()) {
        QByteArray datagram;
        // 调整数据报大小为等待处理的数据报大小
        datagram.resize(udpSocket->pendingDatagramSize());

        // 读取数据报
        udpSocket->readDatagram(datagram.data(), datagram.size());

        QDataStream in(&datagram, QIODeviceBase::ReadOnly);

        int messageType;
        in >> messageType;
        QString userName, localHostName, ipAddress, message;
        // 获取当前时间
        QString time=  QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
        switch (messageType) {
        case Message:{
            // 对于普通消息，读取并显示消息内容
            in >> userName >> localHostName >> ipAddress >> message;
            ui->messageBrowser->setTextColor(Qt::blue);
            ui->messageBrowser->setCurrentFont(QFont("Times New Roman", 12));
            ui->messageBrowser->append("[" + userName + "]" + time);
            ui->messageBrowser->append(message);
            break;
        }
        case NewParticipant:
            // 对于新用户加入消息，调用 newParticipant 函数处理
            in >> userName >> localHostName >> ipAddress;
            newParticipant(userName, localHostName, ipAddress);
            break;
        case ParticipantLeft:
            // 对于用户离开消息，调用 participantLeft 函数处理
            in >> userName >> localHostName;
            participantLeft(userName, localHostName, time);
        case Filename:{
            // 对于文件名消息，读取相关信息并调用 hasPendingFile 函数处理
            in >> userName >> localHostName >> ipAddress;
            QString clientAddress, fileName;
            in >> clientAddress >> fileName;
            hasPendingFile(userName, ipAddress, clientAddress, fileName);
            break;
        }
        case Refuse:{
            // 对于拒绝消息，读取相关信息并处理
            in >> userName >> localHostName;
            QString serverAddress;
            in >> serverAddress;
            QString ipAddress = getIP();
            if (ipAddress == serverAddress)
                server->refused();
            break;
        }
        }
    }
}


// 点击发送按钮时的处理函数，发送普通消息
void chatWidget::on_sendButton_clicked()
{
    sendMessage(Message);
}


// 点击发送工具按钮时的处理函数
void chatWidget::on_sendToolBtn_clicked()
{
    // 确保用户已经选择了用户列表中的用户，否则弹出警告
    if (ui->userTableWidget->selectedItems().isEmpty()) {
        QMessageBox::warning(0, "选择用户", "请先选择用户列表选择要发送的用户！", QMessageBox::Ok);
        return;
    }
    // 打开文件传输窗口并初始化服务器
    server->show();
    server->initServer();
}


// 按下回车键登录
void chatWidget::keyPressEvent(QKeyEvent* event)
{
    if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter)
    {
        if (ui->sendButton->isEnabled())
        {
            ui->sendButton->click();
        }
    }
    QWidget::keyPressEvent(event);
}
